from flask import Flask, request, jsonify, render_template
import parselmouth
import numpy as np
import os
import base64
import json
import tempfile
from flask_cors import CORS
import wave
import struct
from audio_repository import StandardAudioRepository
from tone_analyzer import analyze_audio_data
from db_config import execute_query
from score_analyzer import ScoreAnalyzer

# 导入API接口
from tone_api import (
    get_all_characters,
    get_characters_by_tone,
    analyze_tone,
    get_character_audio
)

app = Flask(__name__)
CORS(app)  # 启用跨域请求支持

# 注册API接口
app.add_url_rule('/api/characters', view_func=get_all_characters, methods=['GET'])
app.add_url_rule('/api/characters/by-tone/<int:tone>', view_func=get_characters_by_tone, methods=['GET'])
app.add_url_rule('/api/tone-analysis', view_func=analyze_tone, methods=['POST'])
app.add_url_rule('/api/character-audio/<character>/<pinyin>', view_func=get_character_audio, methods=['GET'])

@app.route('/')
def hello_world():
    return render_template('index.html')

@app.route('/api/analyze-pitch', methods=['POST'])
def analyze_pitch():
    try:
        data = request.json
        
        # 检查是否有音频数据
        if 'audioData' not in data:
            return jsonify({'error': '未找到音频数据'}), 400
            
        # 获取音频参数
        audio_data = data.get('audioData')
        sample_rate = data.get('sampleRate', 16000)
        channels = data.get('channels', 1)
        
        # 可选参数
        pitch_floor = float(data.get('pitchFloor', 75.0))  # 基频下限，默认75Hz
        pitch_ceiling = float(data.get('pitchCeiling', 500.0))  # 基频上限，默认500Hz
        
        # 将Base64编码的音频数据解码
        if isinstance(audio_data, str) and audio_data.startswith('data:audio'):
            # 处理dataURL格式
            audio_data = audio_data.split(',')[1]
            
        decoded_data = base64.b64decode(audio_data)
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
            temp_path = temp_file.name
            
            # 如果数据是PCM格式，转换为WAV
            if data.get('format') == 'pcm':
                # 创建WAV文件头
                with wave.open(temp_path, 'wb') as wav_file:
                    wav_file.setnchannels(channels)
                    wav_file.setsampwidth(2)  # 16位音频
                    wav_file.setframerate(sample_rate)
                    wav_file.writeframes(decoded_data)
            else:
                # 直接写入数据（假设已经是WAV格式）
                temp_file.write(decoded_data)
        
        # 使用Parselmouth分析音频
        sound = parselmouth.Sound(temp_path)
        
        # 创建带参数的Pitch对象
        pitch = sound.to_pitch(pitch_floor=pitch_floor, pitch_ceiling=pitch_ceiling)
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤掉未检测到的基频点（值为0）
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq > 0
        ]
        
        # 计算统计信息
        valid_freqs = [item["frequency"] for item in filtered_data]
        
        if not valid_freqs:
            stats = {
                "mean": 0,
                "min": 0,
                "max": 0,
                "count": 0,
                "median": 0,
                "std": 0
            }
        else:
            stats = {
                "mean": float(np.mean(valid_freqs)),
                "min": float(np.min(valid_freqs)),
                "max": float(np.max(valid_freqs)),
                "count": len(valid_freqs),
                "median": float(np.median(valid_freqs)),
                "std": float(np.std(valid_freqs))
            }
        
        # 清理临时文件
        os.unlink(temp_path)
        
        return jsonify({
            "status": "success",
            "data": filtered_data,
            "stats": stats
        })
    
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/analyze-audio-full', methods=['POST'])
def analyze_audio_full():
    """完整分析音频，返回归一化点和拟合参数"""
    try:
        data = request.json
        
        # 检查是否有音频数据
        if 'audioData' not in data:
            return jsonify({'error': '未找到音频数据'}), 400
            
        # 首先获取基频数据
        audio_data = data.get('audioData')
        sample_rate = data.get('sampleRate', 16000)
        channels = data.get('channels', 1)
        format_type = data.get('format', 'wav')
        pitch_floor = float(data.get('pitchFloor', 75.0))
        pitch_ceiling = float(data.get('pitchCeiling', 500.0))
        
        # 将Base64编码的音频数据解码
        if isinstance(audio_data, str) and audio_data.startswith('data:audio'):
            # 处理dataURL格式
            audio_data = audio_data.split(',')[1]
            
        decoded_data = base64.b64decode(audio_data)
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
            temp_path = temp_file.name
            
            # 如果数据是PCM格式，转换为WAV
            if format_type == 'pcm':
                # 创建WAV文件头
                with wave.open(temp_path, 'wb') as wav_file:
                    wav_file.setnchannels(channels)
                    wav_file.setsampwidth(2)  # 16位音频
                    wav_file.setframerate(sample_rate)
                    wav_file.writeframes(decoded_data)
            else:
                # 直接写入数据（假设已经是WAV格式）
                temp_file.write(decoded_data)
        
        # 使用Parselmouth分析音频
        sound = parselmouth.Sound(temp_path)
        
        # 创建带参数的Pitch对象
        pitch = sound.to_pitch(pitch_floor=pitch_floor, pitch_ceiling=pitch_ceiling)
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤掉未检测到的基频点（值为0）
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq > 0
        ]
        
        # 清理临时文件
        os.unlink(temp_path)
        
        if not filtered_data:
            return jsonify({
                "status": "error",
                "message": "未检测到有效的基频数据"
            }), 400
            
        result_data = {
            "status": "success",
            "data": filtered_data
        }
        
        # 对基频数据进行分析，获取归一化点和拟合参数
        analysis_result = analyze_audio_data(result_data)
        
        # 检查是否需要查询标准数据
        if 'character' in request.json and 'pinyin' in request.json:
            character = request.json.get('character')
            pinyin = request.json.get('pinyin')
            
            # 查询标准数据
            standard_audio = StandardAudioRepository.get_by_character_and_pinyin(character, pinyin)
            
            if standard_audio and standard_audio.get('frequency_data'):
                # 解析标准数据
                try:
                    standard_freq_data = json.loads(standard_audio['frequency_data'])
                    standard_analysis = analyze_audio_data(standard_freq_data)
                    
                    if standard_analysis.get('status') == 'success':
                        analysis_result['standard_data'] = {
                            'normalized_points': standard_analysis['normalized_points'],
                            'fit_params': standard_analysis['fit_params'],
                            'fit_curve_points': standard_analysis['fit_curve_points'],
                            'tone_type': standard_analysis['tone_type']
                        }
                except Exception as e:
                    analysis_result['standard_data_error'] = str(e)
        
        return jsonify(analysis_result)
        
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/standard-audios', methods=['GET'])
def get_standard_audios():
    """获取所有标准音频数据"""
    try:
        standard_audios = StandardAudioRepository.get_all()
        return jsonify({
            "status": "success",
            "data": standard_audios
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/standard-audio/<character>/<pinyin>', methods=['GET'])
def get_standard_audio(character, pinyin):
    """获取指定汉字和拼音的标准音频数据"""
    try:
        standard_audio = StandardAudioRepository.get_by_character_and_pinyin(character, pinyin)
        
        if not standard_audio:
            return jsonify({
                "status": "error",
                "message": f"未找到汉字'{character}'拼音'{pinyin}'的标准音频数据"
            }), 404
            
        # 解析频率数据
        if standard_audio.get('frequency_data'):
            try:
                standard_audio['frequency_data'] = json.loads(standard_audio['frequency_data'])
            except:
                pass
        
        return jsonify({
            "status": "success",
            "data": standard_audio
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/standard-audio/<int:audio_id>', methods=['GET'])
def get_standard_audio_by_id(audio_id):
    """根据ID获取标准音频数据"""
    try:
        standard_audio = StandardAudioRepository.get_by_id(audio_id)
        
        if not standard_audio:
            return jsonify({
                "status": "error",
                "message": f"未找到ID为{audio_id}的标准音频数据"
            }), 404
            
        # 解析频率数据
        if standard_audio.get('frequency_data'):
            try:
                standard_audio['frequency_data'] = json.loads(standard_audio['frequency_data'])
            except:
                pass
        
        return jsonify({
            "status": "success",
            "data": standard_audio
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/standard-audio', methods=['POST'])
def save_standard_audio():
    """保存标准音频数据"""
    try:
        data = request.json
        
        # 验证必填字段
        required_fields = ['character_text', 'pinyin', 'tone_type']
        for field in required_fields:
            if field not in data:
                return jsonify({
                    "status": "error",
                    "message": f"缺少必填字段: {field}"
                }), 400
        
        # 获取字段值
        character_text = data.get('character_text')
        pinyin = data.get('pinyin')
        tone_type = data.get('tone_type')
        audio_path = data.get('audio_path', '')
        frequency_data = data.get('frequency_data', {})
        
        # 保存数据
        StandardAudioRepository.save_standard_audio(
            character_text, pinyin, tone_type, audio_path, frequency_data
        )
        
        return jsonify({
            "status": "success",
            "message": "标准音频数据保存成功"
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/generate-tone-curve', methods=['POST'])
def generate_tone_curve():
    try:
        data = request.json
        if not data or 'data' not in data:
            return jsonify({'error': '未找到基频数据'}), 400
        # 声调类型（可选参数）
        tone_type = request.args.get('tone_type', '声调曲线分析')
        # 分析数据而不是绘制图像
        analysis_result = analyze_tone_data(data)
        return jsonify({
            "status": "success",
            "analysis": analysis_result
        })
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/pcm-to-wav', methods=['POST'])
def pcm_to_wav():
    try:
        if 'audio' not in request.files:
            return jsonify({'error': '未找到音频文件'}), 400
            
        pcm_file = request.files['audio']
        sample_rate = int(request.form.get('sampleRate', 16000))
        channels = int(request.form.get('channels', 1))
        
        # 读取PCM数据
        pcm_data = pcm_file.read()
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_wav:
            temp_wav_path = temp_wav.name
            
            # 创建WAV文件
            with wave.open(temp_wav_path, 'wb') as wav_file:
                wav_file.setnchannels(channels)
                wav_file.setsampwidth(2)  # 16位音频
                wav_file.setframerate(sample_rate)
                wav_file.writeframes(pcm_data)
        
        # 分析音频
        sound = parselmouth.Sound(temp_wav_path)
        pitch = sound.to_pitch()
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤掉未检测到的基频点
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq != 0
        ]
        
        # 清理临时文件
        os.unlink(temp_wav_path)
        
        return jsonify({
            "status": "success",
            "data": filtered_data
        })
        
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/tone-score-euclidean', methods=['POST'])
def tone_score_euclidean():
    """使用欧氏距离算法进行声调评分"""
    try:
        data = request.json
        
        # 检查必要参数
        if 'audioData' not in data:
            return jsonify({'error': '未找到音频数据'}), 400
        if 'character' not in data:
            return jsonify({'error': '未找到汉字参数'}), 400
        if 'pinyin' not in data:
            return jsonify({'error': '未找到拼音参数'}), 400
            
        # 获取参数
        audio_data = data.get('audioData')
        character = data.get('character')
        pinyin = data.get('pinyin')
        sample_rate = data.get('sampleRate', 16000)
        channels = data.get('channels', 1)
        pitch_floor = float(data.get('pitchFloor', 75.0))
        pitch_ceiling = float(data.get('pitchCeiling', 500.0))
        
        # 处理音频数据
        if isinstance(audio_data, str) and audio_data.startswith('data:audio'):
            audio_data = audio_data.split(',')[1]
            
        decoded_data = base64.b64decode(audio_data)
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
            temp_path = temp_file.name
            
            if data.get('format') == 'pcm':
                with wave.open(temp_path, 'wb') as wav_file:
                    wav_file.setnchannels(channels)
                    wav_file.setsampwidth(2)
                    wav_file.setframerate(sample_rate)
                    wav_file.writeframes(decoded_data)
            else:
                temp_file.write(decoded_data)
        
        # 使用Parselmouth分析音频
        sound = parselmouth.Sound(temp_path)
        pitch = sound.to_pitch(pitch_floor=pitch_floor, pitch_ceiling=pitch_ceiling)
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤有效数据
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq > 0
        ]
        
        # 清理临时文件
        os.unlink(temp_path)
        
        if not filtered_data:
            return jsonify({
                "status": "error",
                "message": "未检测到有效的基频数据"
            }), 400
        
        # 构建音频数据格式
        user_audio_data = {
            "status": "success",
            "data": filtered_data
        }
        
        # 进行评分分析
        result = ScoreAnalyzer.analyze_tone_score(user_audio_data, character, pinyin)
        
        if result.get("status") == "error":
            return jsonify(result), 400
        
        # 只返回欧氏距离的评分结果
        euclidean_result = result.get("euclidean_score", {})
        
        return jsonify({
            "status": "success",
            "character": character,
            "pinyin": pinyin,
            "tone_type": result.get("tone_type"),
            "score": euclidean_result.get("score", 0),
            "level": euclidean_result.get("level", "极差"),
            "distance": euclidean_result.get("distance", 0),
            "algorithm": "欧氏距离",
            "details": euclidean_result
        })
        
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/tone-score-dtw', methods=['POST'])
def tone_score_dtw():
    """使用DTW算法进行声调评分"""
    try:
        data = request.json
        
        # 检查必要参数
        if 'audioData' not in data:
            return jsonify({'error': '未找到音频数据'}), 400
        if 'character' not in data:
            return jsonify({'error': '未找到汉字参数'}), 400
        if 'pinyin' not in data:
            return jsonify({'error': '未找到拼音参数'}), 400
            
        # 获取参数
        audio_data = data.get('audioData')
        character = data.get('character')
        pinyin = data.get('pinyin')
        sample_rate = data.get('sampleRate', 16000)
        channels = data.get('channels', 1)
        pitch_floor = float(data.get('pitchFloor', 75.0))
        pitch_ceiling = float(data.get('pitchCeiling', 500.0))
        
        # 处理音频数据
        if isinstance(audio_data, str) and audio_data.startswith('data:audio'):
            audio_data = audio_data.split(',')[1]
            
        decoded_data = base64.b64decode(audio_data)
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
            temp_path = temp_file.name
            
            if data.get('format') == 'pcm':
                with wave.open(temp_path, 'wb') as wav_file:
                    wav_file.setnchannels(channels)
                    wav_file.setsampwidth(2)
                    wav_file.setframerate(sample_rate)
                    wav_file.writeframes(decoded_data)
            else:
                temp_file.write(decoded_data)
        
        # 使用Parselmouth分析音频
        sound = parselmouth.Sound(temp_path)
        pitch = sound.to_pitch(pitch_floor=pitch_floor, pitch_ceiling=pitch_ceiling)
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤有效数据
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq > 0
        ]
        
        # 清理临时文件
        os.unlink(temp_path)
        
        if not filtered_data:
            return jsonify({
                "status": "error",
                "message": "未检测到有效的基频数据"
            }), 400
        
        # 构建音频数据格式
        user_audio_data = {
            "status": "success",
            "data": filtered_data
        }
        
        # 进行评分分析
        result = ScoreAnalyzer.analyze_tone_score(user_audio_data, character, pinyin)
        
        if result.get("status") == "error":
            return jsonify(result), 400
        
        # 只返回DTW的评分结果
        dtw_result = result.get("dtw_score", {})
        
        return jsonify({
            "status": "success",
            "character": character,
            "pinyin": pinyin,
            "tone_type": result.get("tone_type"),
            "score": dtw_result.get("score", 0),
            "level": dtw_result.get("level", "极差"),
            "distance": dtw_result.get("distance", 0),
            "algorithm": "DTW",
            "details": dtw_result
        })
        
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

@app.route('/api/tone-score-combined', methods=['POST'])
def tone_score_combined():
    """使用两种算法进行声调评分，返回综合结果"""
    try:
        data = request.json
        
        # 检查必要参数
        if 'audioData' not in data:
            return jsonify({'error': '未找到音频数据'}), 400
        if 'character' not in data:
            return jsonify({'error': '未找到汉字参数'}), 400
        if 'pinyin' not in data:
            return jsonify({'error': '未找到拼音参数'}), 400
            
        # 获取参数
        audio_data = data.get('audioData')
        character = data.get('character')
        pinyin = data.get('pinyin')
        sample_rate = data.get('sampleRate', 16000)
        channels = data.get('channels', 1)
        pitch_floor = float(data.get('pitchFloor', 75.0))
        pitch_ceiling = float(data.get('pitchCeiling', 500.0))
        
        # 处理音频数据
        if isinstance(audio_data, str) and audio_data.startswith('data:audio'):
            audio_data = audio_data.split(',')[1]
            
        decoded_data = base64.b64decode(audio_data)
        
        # 创建临时WAV文件
        with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
            temp_path = temp_file.name
            
            if data.get('format') == 'pcm':
                with wave.open(temp_path, 'wb') as wav_file:
                    wav_file.setnchannels(channels)
                    wav_file.setsampwidth(2)
                    wav_file.setframerate(sample_rate)
                    wav_file.writeframes(decoded_data)
            else:
                temp_file.write(decoded_data)
        
        # 使用Parselmouth分析音频
        sound = parselmouth.Sound(temp_path)
        pitch = sound.to_pitch(pitch_floor=pitch_floor, pitch_ceiling=pitch_ceiling)
        
        # 获取基频和时间信息
        pitch_values = pitch.selected_array['frequency'].tolist()
        timestamps = pitch.xs().tolist()
        
        # 过滤有效数据
        filtered_data = [
            {"time": time, "frequency": freq} 
            for time, freq in zip(timestamps, pitch_values) 
            if freq > 0
        ]
        
        # 清理临时文件
        os.unlink(temp_path)
        
        if not filtered_data:
            return jsonify({
                "status": "error",
                "message": "未检测到有效的基频数据"
            }), 400
        
        # 构建音频数据格式
        user_audio_data = {
            "status": "success",
            "data": filtered_data
        }
        
        # 进行评分分析
        result = ScoreAnalyzer.analyze_tone_score(user_audio_data, character, pinyin)
        
        if result.get("status") == "error":
            return jsonify(result), 400
        
        return jsonify(result)
        
    except Exception as e:
        return jsonify({
            "status": "error",
            "message": str(e)
        }), 500

if __name__ == '__main__':
    app.run(debug=True)
